#include "ParticleSystem.h"
#include "..\\tinyXML\\tinyxml.h"
#include "GameEngine.h"

// <ParticleSystem Name = "Fire">
// <count>100</count>
// <lifetime>800</lifetime>
// <colors>
//    <color time = 50>255,255,255</color>
//    <color time = 200>255, 255,0 </color>
//    <color time = 800>255, 0,0 </color>
// </colors>
// <alphaChannel type="linear">
//    <alpha time=50>255</alpha>
//    <alpha time=300>0</alpha>
// </alphaChannel>
// <speeds>
//    <speed time = 800>5</speed>
// <speeds>
// <images>
//    <image>fire1</image>
//    <image>fire2</image>
// </images>
// </ParticleSystem>
int StrToInt(string str)
{
    return atoi(str.c_str());
}

float StrToFloat(string str)
{
    // TODO 
    float data = 0;
    sscanf_s(str.c_str(),"%f",&data);
    return data;
}
#define BEGIN_PARSE(n)  level[n] = level[n-1]->FirstChildElement(); \
                        do  \
                        {

#define END_PARSE(n)    if (level[n] == level[n-1]->LastChild()) break; \
                        level[n] = level[n]->NextSiblingElement();  \
                        } while (true);  

CParticleSystem::CParticleSystem(string filename)
{    
    m_pool = NULL;
    m_bEnabled = FALSE;
    m_Object = NULL;
    m_x = 0;
    m_y = 0;
    m_dwTimeForCreateNew = 50;

    TiXmlDocument  doc;    
    doc.LoadFile(filename.c_str());
    TiXmlElement *level[32];

    level[0] = doc.RootElement();

    string strParticleSystem = string("ParticleSystem");    
   
    BEGIN_PARSE(1)
        string Value = string(level[1]->Value());
        if (Value == strParticleSystem)
        {
            const char *name  = level[1]->Attribute("Name");            
            BEGIN_PARSE(2)
                string Value = string(level[2]->Value());
                LPCSTR text = level[2]->GetText();                
                string content = (text)?text:"";
                if (Value == string("count"))
                {
                    m_Count = StrToInt(content);
                }else if (Value == string("lifetime")){
                    m_dwLifeTime = StrToInt(content);
                }else if (Value == string("colors")){
                    BEGIN_PARSE(3)
                        string Value = string(level[3]->Value());
                        string content = level[3]->GetText();
                        if (Value == string("color"))
                        {
                            const char *time  = level[3]->Attribute("time");
                            RGB rgb;                            
                            ParseRGB(content, rgb);
                            rgb.time = StrToInt(time);
                            m_ColorChanges.insert(rgb);
                        }
                    END_PARSE(3)
                }else if (Value == string("alphaChannel")){
                    BEGIN_PARSE(3)
                        string Value = string(level[3]->Value());
                        string content = level[3]->GetText();
                        if (Value == string("alpha"))
                        {
                            const char *time  = level[3]->Attribute("time");
                            ALPHA a;                            
                            a.Alpha = StrToInt(content);
                            a.time = StrToInt(time);
                            m_AlphaChanges.insert(a);
                        }
                    END_PARSE(3)
                }else if (Value == string("speeds")){
                    BEGIN_PARSE(3)
                        string Value = string(level[3]->Value());
                        LPCSTR text = level[3]->GetText();                
                        string content = (text)?text:"";
                        if (Value == string("speed"))
                        {
                            const char *time  = level[3]->Attribute("time");
                            SPEED s;                            
                            s.Speed = StrToFloat(content);
                            s.time = StrToInt(time);
                            m_SpeedChanges.insert(s);
                        }
                    END_PARSE(3)
                }else if (Value == string("images")){
                    BEGIN_PARSE(3)
                        string Value = string(level[3]->Value());
                        string content = level[3]->GetText();
                        if (Value == string("image"))
                        {                                                        
                            m_lstUsedImages.push_back(ResourceManager::GetResource(content));
                        }
                    END_PARSE(3)
                }
            END_PARSE(2)          
        }
    END_PARSE(1)
}

CParticleSystem::~CParticleSystem(void)
{
    Cleanup();  
}

VOID CParticleSystem::ParseRGB( string content, RGB& rgb)
{   
    int r;
    int g;
    int b;
    sscanf(content.c_str(), "%d %d %d", &r, &g, &b);    
    rgb.R = r;
    rgb.G = g;
    rgb.B = b;
}

//TODO: CParticleSystem::Start
void CParticleSystem::Start( int x, int y, float fAngle )
{
    m_bEnabled = TRUE;
    m_x = x;
    m_y = y;
    m_Angle = fAngle;
}

void CParticleSystem::Attach( TGameObject *object )
{
    m_Object = object;    
}

void CParticleSystem::Update()
{
    if (m_bEnabled)
    {    
        if (m_TimerGenerateNew.IsFinished())
        {
            m_TimerGenerateNew.Start(m_dwTimeForCreateNew);
            CElement *el = (CElement *)m_pool->CreateObject();
            if (el)
            {
                m_lstActiveElements.push_back(el);            
            }        
        }
    }

    list<CElement*> lstRemoved;
    list<CElement*>::iterator it = m_lstActiveElements.begin();
    for(;it != m_lstActiveElements.end();it++)
    {
        BOOL bFreed = (*it)->Update();
        if (bFreed)
        {
            lstRemoved.push_back(*it);
            m_pool->FreeObject(*it);
        }
    }
    it = lstRemoved.begin();
    for(;it != lstRemoved.end();it++)
    {
        m_lstActiveElements.remove(*it);
    }
    lstRemoved.clear();
}

void CParticleSystem::Preload()
{
    Cleanup();
    vector<IPoolItem*> elements;
    for (int i=0; i< m_Count;i++)
    {
        IPoolItem *item = new CElement(this);        
        elements.push_back(item);
    }
    m_pool = new Pool(elements);
}

void CParticleSystem::Cleanup()
{
    if (m_pool) 
    {
        delete m_pool;    
        m_pool = NULL;
    }
    
    //list<CElement*>::iterator it = m_lstActiveElements.begin();
    //for(; it != m_lstActiveElements.end();it++)
    //{
    //    delete *it;
    //}
    m_lstActiveElements.clear();
}

void CParticleSystem::SetEnabled( BOOL bEnabled )
{
    m_bEnabled = bEnabled;
}

void CParticleSystem::SetAngle( float fAngle )
{
    m_Angle = fAngle;
}

int CParticleSystem::GetX()
{
    if (m_Object)
    {
        TGDKSprite *s = m_Object->GetMainSprite();        
        int x = s->GetX();// - dbCOS(s->GetAngle()) * s->GetOffsetX();
        return x;
    }
    return m_x;
}

int CParticleSystem::GetY()
{
    if (m_Object)
    {
        TGDKSprite *s = m_Object->GetMainSprite();        
        return s->GetY();// - dbSIN(s->GetAngle()) * s->GetOffsetY();
    }
    return m_y;
}

void CParticleSystem::SetTimeGeneration( DWORD time )
{
    m_dwTimeForCreateNew = time;
}

//////////////////////////////////////////////////////////////////////////
//CElement
//////////////////////////////////////////////////////////////////////////

CParticleSystem::CElement::CElement( CParticleSystem* system)
{
    this->system = system;
    TGDKImage& img = *system->m_lstUsedImages[rand()%system->m_lstUsedImages.size()];
    spr = new TGDKSprite(img, NULL, 0, 0);  
    spr->Hide();    
    m_bActive = FALSE;
}

CParticleSystem::CElement::~CElement()
{
    delete spr;
}


void CParticleSystem::CElement::Reset()
{
    m_CurrentSpeed = system->m_SpeedChanges.begin();
    m_CurrentAlpha = system->m_AlphaChanges.begin();
    m_NextAlpha = m_CurrentAlpha;
    m_NextAlpha++;
    m_CurrentColor = system->m_ColorChanges.begin();
    m_NextColor = m_CurrentColor;
    m_NextColor++;

    if (system->m_Object)
    {
        spr->SetOffset(spr->GetWidth()/2 +system->m_x, spr->GetHeight()/2 + system->m_y);
    } 

    spr->XPos = system->GetX() + rand()%10-5;
    spr->YPos = system->GetY() + rand()%10-5;
    spr->Refresh();
    spr->SetAngle(system->m_Angle + rand()%10-5);
    spr->Show();
    RGB& rgb = *m_CurrentColor;
    spr->SetDiffuse(rgb.R, rgb.G, rgb.B);

    ALPHA& alpha = *m_CurrentAlpha;
    spr->SetAlpha(alpha.Alpha);

    m_SpeedValue = (*m_CurrentSpeed).Speed;

    m_dwStartLive = GameEngine::GetTime();
    m_bActive = TRUE;
}

void CParticleSystem::CElement::Free()
{
    m_bActive = FALSE;
}

BOOL CParticleSystem::CElement::Update()
{
    if (!m_bActive) return FALSE;
    spr->Move(m_SpeedValue);

    DWORD timeLeft = GameEngine::GetTime() - m_dwStartLive;

    if (m_CurrentSpeed != system->m_SpeedChanges.end() && (*m_CurrentSpeed).time < timeLeft) 
    {
        m_CurrentSpeed++;
        if (m_CurrentSpeed != system->m_SpeedChanges.end())
        {
            m_SpeedValue = (*m_CurrentSpeed).Speed;
        }        
    }

    if (m_CurrentColor != system->m_ColorChanges.end())
    {    
        if ((*m_CurrentColor).time < timeLeft) 
        {
            m_CurrentColor++;
            if (m_CurrentColor != system->m_ColorChanges.end())
            {
                m_NextColor = m_CurrentColor;
                m_NextColor++;
                RGB& rgb = *m_CurrentColor;
                spr->SetDiffuse(rgb.R, rgb.G, rgb.B);
            }
        }else if (m_NextColor != system->m_ColorChanges.end()){
            RGB& rgbCur = *m_CurrentColor;
            RGB& rgbNext = *m_NextColor;
            float p = (float)(rgbCur.time - timeLeft)/(float)(rgbNext.time-rgbCur.time);
            int r = rgbCur.R * p + rgbNext.R *(1.0f - p);
            int g = rgbCur.G * p + rgbNext.G *(1.0f - p);
            int b = rgbCur.B * p + rgbNext.B *(1.0f - p);
            spr->SetDiffuse(r, g, b);
        }
    }

    if (m_CurrentAlpha != system->m_AlphaChanges.end())
    {
        if ((*m_CurrentAlpha).time < timeLeft) 
        {
            m_CurrentAlpha++;
            if (m_CurrentAlpha != system->m_AlphaChanges.end())
            {
                m_NextAlpha = m_CurrentAlpha;
                m_NextAlpha++;
                ALPHA& alpha = *m_CurrentAlpha;
                spr->SetAlpha(alpha.Alpha);
            }
        }else if (m_NextAlpha != system->m_AlphaChanges.end()){
            ALPHA& aCur = *m_CurrentAlpha;
            ALPHA& aNext = *m_NextAlpha;
            float p = (float)(aCur.time - timeLeft)/(float)(aNext.time-aCur.time);
            int a = aCur.Alpha * p + aNext.Alpha *(1.0f - p);
            spr->SetAlpha(a);
        }
    }

    return (timeLeft > system->m_dwLifeTime);
}
